package com.ra.pay.alipay;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayUserDetail;
import com.alipay.api.request.AlipaySystemOauthTokenRequest;
import com.alipay.api.request.AlipayUserGetRequest;
import com.alipay.api.response.AlipaySystemOauthTokenResponse;
import com.alipay.api.response.AlipayUserGetResponse;

/**
 * 
 * 微信功能封装
 * 
 * @author 沈康
 * @date 2015年5月12日
 */
public class AlipayUtil {

	private static final Log logger = LogFactory.getLog(AlipayUtil.class);

	// 支付宝提供给商户的服务接入网关URL(新)
	private static final String ALIPAY_GATEWAY_NEW = "https://mapi.alipay.com/gateway.do?";

	// 线上地址
	private static final String ALIPAY_ONLINE_URL = "https://openapi.alipay.com/gateway.do";

	// 沙箱地址
	//private static final String ALIPAY_SANDBOX_URL = "http://openapi.alipaydev.com/gateway.do";
	
	// 初始化客户端
	private static final AlipayClient client = new DefaultAlipayClient(
			ALIPAY_ONLINE_URL, "应用ID", "应用私钥", "json");

	/**
	 * 用于防钓鱼，调用接口query_timestamp来获取时间戳的处理函数 注意：远程解析XML出错，与服务器是否支持SSL等配置有关
	 * 
	 * @return 时间戳字符串
	 * @throws IOException
	 * @throws DocumentException
	 * @throws MalformedURLException
	 */
	@SuppressWarnings("unchecked")
	public static String query_timestamp() throws MalformedURLException,
			DocumentException, IOException {
		String strUrl = ALIPAY_GATEWAY_NEW + "service=query_timestamp&partner="
				+ AlipayConfig.PARENER + "&_input_charset"
				+ AlipayConfig.INPUT_CHARSET;
		StringBuffer result = new StringBuffer();

		SAXReader reader = new SAXReader();
		Document doc = reader.read(new URL(strUrl).openStream());

		List<Node> nodeList = doc.selectNodes("//alipay/*");
		for (Node node : nodeList) {
			// 截取部分不需要解析的信息
			if (node.getName().equals("is_success")
					&& node.getText().equals("T")) {
				// 判断是否有成功标示
				List<Node> nodeList1 = doc
						.selectNodes("//response/timestamp/*");
				for (Node node1 : nodeList1) {
					result.append(node1.getText());
				}
			}
		}
		return result.toString();
	}

	/**
     * 生成签名结果
     * @param sPara 要签名的数组
     * @return 签名结果字符串
     */
	public static String buildRequestMysign(Map<String, String> sPara, String signType) {
		//把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串
    	String prestr = AlipayCore.createLinkString(sPara); 
    	String mysign = "";
        if(AlipayConfig.SIGN_TYPE_MD5.equals(signType)) {
        	mysign = AlipayMd5Encrypt.sign(prestr, AlipayConfig.KEY, AlipayConfig.INPUT_CHARSET);
        }
        if(AlipayConfig.SIGN_TYPE_RSA.equals(signType)){
        	mysign = AlipayRSAEncrypt.sign(prestr, AlipayConfig.PRIVATE_KEY, AlipayConfig.INPUT_CHARSET);
        }
        return mysign;
    }

	/**
	 * 支付宝令牌：用于应用对支付宝相关API查询的授权例如：获取支付宝用户信息
	 * 获取支付宝授权令牌
	 * @return 授权令牌
	 */
	public static String getAuthToken() {
		AlipaySystemOauthTokenRequest req = new AlipaySystemOauthTokenRequest();
		// 根据授权码取授权令牌
		req.setGrantType("authorization_code");
		// 授权码，用户对应用授权后得到。
		req.setCode("4b203fe6c11548bcabd8da5bb087a83b");
		AlipaySystemOauthTokenResponse response = null;
		try {
			response = client.execute(req);
		} catch (AlipayApiException e) {
			logger.info("获取授权令牌出错", e);
		}
		return response.getAccessToken();
	}

	/**
	 * 刷新令牌
	 * @return 刷新令牌
	 */
	public static String refreshToken() {
		AlipaySystemOauthTokenRequest req=new AlipaySystemOauthTokenRequest();
		// 用刷新令牌取授权令牌
		req.setGrantType("refresh_token");
		// 刷新令牌
		req.setRefreshToken("201208134b203fe6c11548bcabd8da5bb087a83b");
		AlipaySystemOauthTokenResponse response = null;
		try {
			response = client.execute(req);
		} catch (AlipayApiException e) {
			logger.info("刷新授权令牌出错", e);
		} 
		return response.getRefreshToken();
	}
	
	/**
	 * 获取支付宝用户信息
	 * @return 用户信息
	 */
	public AlipayUserDetail getAlipayUserInfo() {
		AlipayUserGetRequest req = new AlipayUserGetRequest();
		AlipayUserGetResponse res = null;
		try {
			res = client.execute(req, getAuthToken());
		} catch (AlipayApiException e) {
			e.printStackTrace();
		} 
		return res.getAlipayUserDetail();
	}
	
	/**
     * 解析远程模拟提交后返回的信息，获得token
     * @param text 要解析的字符串
     * @return 解析结果
     * @throws Exception 
     */
    public static String decryptRequestToken(String text) throws Exception {
    	String request_token = "";
    	//以“&”字符切割字符串
    	String[] strSplitText = text.split("&");
    	//把切割后的字符串数组变成变量与数值组合的字典数组
    	Map<String, String> paraText = new HashMap<String, String>();
        for (int i = 0; i < strSplitText.length; i++) {

    		//获得第一个=字符的位置
        	int nPos = strSplitText[i].indexOf("=");
        	//获得字符串长度
    		int nLen = strSplitText[i].length();
    		//获得变量名
    		String strKey = strSplitText[i].substring(0, nPos);
    		//获得数值
    		String strValue = strSplitText[i].substring(nPos+1,nLen);
    		//放入MAP类中
    		paraText.put(strKey, strValue);
        }

    	if (paraText.get("res_data") != null) {
    		String res_data = paraText.get("res_data");
    		
    		//解析加密部分字符串（RSA与MD5区别仅此一句）
    		if(paraText.get("sec_id").equals(AlipayConfig.SIGN_TYPE_RSA)) {
    			res_data = AlipayRSAEncrypt.decrypt(res_data, AlipayConfig.PRIVATE_KEY, AlipayConfig.INPUT_CHARSET);
    		}
    		
    		//token从res_data中解析出来（也就是说res_data中已经包含token的内容）
    		Document document = DocumentHelper.parseText(res_data);
    		request_token = document.selectSingleNode( "//direct_trade_create_res/request_token" ).getText();
    	}
    	return request_token;
    }
    
    /**
     * 生成要请求给支付宝的参数数组
     * @param sParaTemp 请求前的参数数组
     * @return 要请求的参数数组
     */
    public static Map<String, String> buildRequestPara(Map<String, String> paramsMap, String signType) {
    	// 除去数组中的空值和签名参数
    	Map<String, String> sPara = AlipayCore.paraFilter(paramsMap);
    			
    	// 生成签名结果
    	String mysign = buildRequestMysign(sPara, signType);

        //签名结果与签名方式加入请求提交参数组中
        sPara.put("sign", mysign);
        if(! sPara.get("service").equals("alipay.wap.trade.create.direct") && ! sPara.get("service").equals("alipay.wap.auth.authAndExecute")) {
        	sPara.put("sign_type", signType);
        }
        return sPara;
    }
}
